package org.classdef.scan;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.classdef.analyzer.ClassAnalyzer;
import org.classdef.analyzer.info.ClassDef;
import org.classdef.scan.checker.DefaultClassRuleChecker;
import org.classdef.scan.checker.IClassRuleChecker;
import org.classdef.scan.print.ConsoleResultPrinter;
import org.classdef.scan.print.FileResultPrinter;
import org.classdef.scan.print.IResultPrinter;

/**
 * 
 * 文件扫描类
 *
 */
public class ClassScanner {
	
	private String scanFile;
	private IClassRuleChecker checker;
	private IResultPrinter printer;
	
	private ClassAnalyzer analyzer;
	
	private int dirCnt;
	private int classCnt;
	private int jarCnt;
	private int jarEntryCnt;
	private int analyzerCnt;
	private int analyzeFailCnt;
	

	public ClassScanner(String scanFile){
		this(scanFile, new ConsoleResultPrinter(System.out), new DefaultClassRuleChecker());
	}
	
	public ClassScanner(String scanFile, String output){
		this(scanFile, output == null ? null : new FileResultPrinter(output), new DefaultClassRuleChecker());
	}
	public ClassScanner(String scanFile, String output, IClassRuleChecker checker){
		this(scanFile, output == null ? null : new FileResultPrinter(output), checker);
	}
	
	public ClassScanner(String scanFile, IResultPrinter printer, IClassRuleChecker checker){
		this.checker = checker == null ? new DefaultClassRuleChecker() : checker;
		this.printer = printer == null ? new ConsoleResultPrinter(System.out) : printer;
		this.scanFile = scanFile;
		this.analyzer = new ClassAnalyzer();
	}
	
	public void scan(){
		File file = new File(scanFile);
		if(!file.exists()){
			err("目标文件或目录["+scanFile+"]不存在！");
			return;
		}

		this.checker.start();
		this.printer.start();
		long t1 = System.currentTimeMillis();
		long t2 = 0;
		try {
			if(file.isDirectory()){
				scanDir(file);
			}else if(file.getName().endsWith(".class")){
				scanFile(file);
			}else if(file.getName().endsWith(".jar")){
				scanJar(file);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			t2 = System.currentTimeMillis();
			this.checker.stop();
			this.printer.stop();
		}
		
		log("============================================================================");
		log("");
		log("共扫描文件夹： " + dirCnt + " 个，class文件 "+classCnt+" 个，jar包 " +jarCnt+" 个，jar包内文件 "+jarEntryCnt+" 个！");
		log("共分析class文件"+analyzerCnt+"个，成功 "+(analyzerCnt - analyzeFailCnt)+" 个， 失败 "+analyzeFailCnt+" 个！");
		log("耗时：" + (t2 - t1) + "毫秒！");
	}
	
	private void scanDir(File dirFile) {
		log("开始扫描目录： " + dirFile.getPath());
		dirCnt++;
		File[] files = dirFile.listFiles();
		if(files == null || files.length == 0){
			return;
		}
		for (File file : files) {
			if(file.isDirectory()){
				scanDir(file);
			}else if(file.getName().endsWith(".class")){
				scanFile(file);
			}else if(file.getName().endsWith(".jar")){
				scanJar(file);
			}
		}
		log("目录 ["+dirFile.getPath()+"] 扫描完成！\r\n");
	}
	
	private void scanFile(File file) {
		log("开始扫描文件： " + file.getName());
		classCnt++;
		if(checker.check(file)){
			String classname = file.getName().replace(".class", "");
			try {
				log("分析： " + classname);
				analyzerCnt++;
				ClassDef info = analyzer.analyze(file);
//				log("分析结束！");
				if(info != null){
					printer.print(info);
				}
			} catch (Exception e) {
				analyzeFailCnt++;
				log("分析过程出错，错误信息：" + e.getMessage());
			}
		}
	}

	private void scanJar(File file) {
		log("开始扫描jar包： " + file.getName());
		jarCnt++;
		JarFile jarFile = null;
		try {
			jarFile = new JarFile(file);
		} catch (IOException e) {
			log("jar文件读取出错！错误信息：" + e.getMessage());
		}
		if(jarFile == null){
			return;
		}
		
		Enumeration<JarEntry> entries = jarFile.entries();
		while (entries.hasMoreElements()) {
			JarEntry jarEntry = entries.nextElement();
			
			jarEntryCnt++;
			
			String entryName = jarEntry.getName();
			if (jarEntry.isDirectory() || !entryName.endsWith(".class") || !checker.check(jarEntry)) {
				continue;
			}
			InputStream is = null;
			try {
				is = jarFile.getInputStream(jarEntry);
			} catch (IOException e) {
			}
			if(is == null){
				continue;
			}
			String classname = entryName.replaceAll("/", ".").replace(".class", "");
			try {
				log("分析： " + classname);
				analyzerCnt++;
				ClassDef info = analyzer.analyze(is);
//				log("分析完成！");
				if(info != null){
					printer.print(info);
				}
			} catch (Exception e) {
				analyzeFailCnt++;
				err("分析过程出错，错误信息：" + e.getMessage() + "\r\n");
			}
		}
		
		log("jar文件 ["+file.getName()+"] 扫描完成！\r\n");
	}
	
	private void log(String msg){
		System.out.println(msg);
	}
	
	private void err(String msg){
		System.err.println(msg);
	}
	
}
